package servidorbancos.redeneural;

import java.util.ArrayList;
import java.util.List;
import org.neuroph.core.NeuralNetwork;
import org.neuroph.core.learning.SupervisedTrainingElement;
import org.neuroph.core.learning.TrainingSet;
import org.neuroph.nnet.MultiLayerPerceptron;
import org.neuroph.nnet.learning.MomentumBackpropagation;
import org.neuroph.util.TransferFunctionType;
import servidorbancos.resources.Agencia;

/**
 *
 * @author luiz.perez
 */
public final class RedeNeural {

    private double minValLon = -46.77, maxValLon = -46.39;
    private double minValLat = -23.72, maxValLat = -23.39;
    int divisoes = 8;
    List<Agencia> lista;
    String filePath = "mlp/rede.mlp";
    MultiLayerPerceptron rede = (MultiLayerPerceptron) NeuralNetwork.load(filePath);

    public RedeNeural(List<Agencia> lista) {
        this.lista = new ArrayList<Agencia>(lista);
        Treinar();
    }

    public void Treinar() {
        if (rede == null)
        {
            rede = new MultiLayerPerceptron(TransferFunctionType.SIGMOID, 2, 3);
            TrainingSet<SupervisedTrainingElement> conjTreinamento = new TrainingSet<SupervisedTrainingElement>();
            TrainingSet<SupervisedTrainingElement>[] folds = new TrainingSet[divisoes];
            TrainingSet<SupervisedTrainingElement> test = new TrainingSet<SupervisedTrainingElement>();
            TrainingSet<SupervisedTrainingElement> train = new TrainingSet<SupervisedTrainingElement>();
            int acertos = 0, erros = 0;

            for (int i = 0; i < lista.size(); i++) {
                double[] entrada = {NormalizarLat(lista.get(i).getLat()), NormalizarLon(lista.get(i).getLon())};
                double[] saida = {0, 0, 0};
                if (lista.get(i).getZona() < 3) {
                    saida[lista.get(i).getZona()] = 1;
                }
                conjTreinamento.addElement(new SupervisedTrainingElement(entrada, saida));
            }

            for (int i = 0; i < divisoes; i++) {
                folds[i] = new TrainingSet<SupervisedTrainingElement>();
            }

            for (int i = 0; i < conjTreinamento.size(); i++) {
                int mod = i % divisoes;
                folds[mod].addElement((SupervisedTrainingElement) conjTreinamento.elementAt(i));
            }

            MomentumBackpropagation treinamento = new MomentumBackpropagation();
            treinamento.setBatchMode(true);
            treinamento.setLearningRate(0.5);
            treinamento.setMomentum(0.6);
            treinamento.setMaxError(0.000001);
            treinamento.setMaxIterations(100);
            rede.setLearningRule(treinamento);

            for (int j = 0; j < divisoes; j++) {
                test = folds[j];
                for (int k = 0; k < divisoes; k++) {
                    if (k != j) {
                        train.elements().addAll(folds[k].elements());
                    }
                }
                rede.learn(train);
                acertos = 0;
                erros = 0;
                for (SupervisedTrainingElement st : test.elements()) {
                    rede.setInput(st.getInput());
                    rede.calculate();
                    double x = rede.getOutput()[0];
                    if (x < 0.5) {
                        x = 0;
                    } else {
                        x = 1;
                    }
                    if (x == st.getDesiredOutput()[0]) {
                        acertos++;
                    } else {
                        erros++;
                    }
                }
                System.out.println("Acertos: " + acertos + " Erros: " + erros);
            }
            rede.save(filePath);
        }
        else
            System.out.println("Rede Neural aberta corretamente");
    }

    public double NormalizarLat(double d) {
        return ((d - minValLat) / (maxValLat - minValLat));
    }

    public double NormalizarLon(double d) {
        return ((d - minValLon) / (maxValLon - minValLon));
    }

    public String calcula(double lat, double lon) {
        double[] petitpipe = {NormalizarLat(lat), NormalizarLon(lon)};
        rede.setInput(petitpipe);
        rede.calculate();
        double[] pipe = rede.getOutput();
        int valmax = 0;
        for (int i = 0; i < pipe.length; i++) {
            if (pipe[i] > pipe[valmax]) {
                valmax = i;
            }
        }
        if (pipe[valmax] < 0.5) {
            return ("Centro.");
        } else {
            switch (valmax) {
                case 0:
                    return ("Norte");
                case 1:
                    return ("Sul");
                case 2:
                    return ("Leste");
                default:
                    return ("Deu Erro.");
            }
        }
    }
}
